


import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;

import javax.swing.JPanel;



public class DrawPanel extends JPanel {

	/**
	 * 
	 */
	private static final long serialVersionUID = -5363056660674236290L;
	private List<Point> points = new ArrayList<Point>();
	private Color color;
	private Wireframe wireframe = Wireframe.getDefaultType();
	private Boolean wireframeEnable = false;
	private Boolean rasterizationEnable = true;
	private Integer numEdges = 3;
	private Color[][] colorMatrix;

	public DrawPanel() {
		color = Color.WHITE;

		addMouseListener(new MouseAdapter() {
			public void mousePressed(MouseEvent e) {
				Point point = new Point(e.getX(), e.getY(), color);
				points.add(point);
				repaint();
			}
		});
	}

	protected void paintComponent(Graphics g) {
		super.paintComponent(g);
		List<Point> polygon = new ArrayList<Point>();
		Point point1, point2;
		for (int i = 0; i < points.size(); i++) {
			polygon.add(points.get(i));
			if (i % numEdges == (numEdges - 1)) {
				for (int j = 0; j < numEdges; j++) {
					point1 = polygon.get(j);
					point2 = polygon.get((j + 1) % numEdges);
					if (rasterizationEnable) {
						drawLine(point1, point2, polygon.get(0), g, false);
					}
					if (wireframeEnable) {
						drawLine(point1, point2, polygon.get(0), g, true);
					}
				}
				polygon.clear();
			}
		}

		for (Point point : polygon) {
			g.setColor(point.getColor());
			g.drawRect(point.getX(), point.getY(), 1, 1);
		}
	}

	public Integer getNumEdges() {
		return numEdges;
	}

	

	private void drawLine(Point point1, Point point2, Point firstPoint,
			Graphics g, Boolean wireframe) {
		int x, y, erro, deltaX, deltaY;
		erro = 0;
		int x1 = point1.getX();
		int x2 = point2.getX();
		int y1 = point1.getY();
		int y2 = point2.getY();

		double fullDist = point1.dist(point2);
		x = x1;
		y = y1;
		deltaX = x2 - x1;
		deltaY = y2 - y1;

		if ((Math.abs(deltaY) >= Math.abs(deltaX) && y1 > y2)
				|| (Math.abs(deltaY) < Math.abs(deltaX) && deltaY < 0)) {

			x = x2;
			y = y2;
			deltaX = x1 - x2;
			deltaY = y1 - y2;
		}
		if (deltaX >= 0) {
			if (Math.abs(deltaX) >= Math.abs(deltaY)) {
				for (int i = 1; i < Math.abs(deltaX); i++) {
					if (erro < 0) {
						x++;
						drawLinePoint(point1, point2, firstPoint, g, x, y,
								fullDist, wireframe);
						erro += deltaY;
					} else {
						x++;
						y++;
						drawLinePoint(point1, point2, firstPoint, g, x, y,
								fullDist, wireframe);
						erro += deltaY - deltaX;
					}
				}
			} else {
				for (int i = 1; i < Math.abs(deltaY); i++) {
					if (erro < 0) {
						x++;
						y++;
						drawLinePoint(point1, point2, firstPoint, g, x, y,
								fullDist, wireframe);
						erro += deltaY - deltaX;
					} else {
						y++;
						drawLinePoint(point1, point2, firstPoint, g, x, y,
								fullDist, wireframe);
						erro -= deltaX;
					}
				}
			}
		} else {
			if (Math.abs(deltaX) >= Math.abs(deltaY)) {
				for (int i = 1; i < Math.abs(deltaX); i++) {
					if (erro < 0) {
						x--;
						drawLinePoint(point1, point2, firstPoint, g, x, y,
								fullDist, wireframe);
						erro += deltaY;
					} else {
						x--;
						y++;
						drawLinePoint(point1, point2, firstPoint, g, x, y,
								fullDist, wireframe);
						erro += deltaY + deltaX;
					}
				}
			} else {
				for (int i = 1; i < Math.abs(deltaY); i++) {
					if (erro < 0) {
						x--;
						y++;
						drawLinePoint(point1, point2, firstPoint, g, x, y,
								fullDist, wireframe);
						erro += deltaY + deltaX;
					} else {
						y++;
						drawLinePoint(point1, point2, firstPoint, g, x, y,
								fullDist, wireframe);
						erro += deltaX;
					}
				}
			}
		}
	}

	private void drawLinePoint(Point point1, Point point2, Point firstPoint,
			Graphics g, int x, int y, double fullDist, Boolean wireframe) {
		if (wireframe && !this.wireframe.shouldDraw())
			return;
		
		Component component = this.getComponentAt(x, y);
		int width = component.getWidth();
		int height = component.getHeight();
		if (this.colorMatrix == null || this.colorMatrix.length != width || this.colorMatrix[0].length != height) {
			this.colorMatrix = new Color[width][height];
		}

		double dist;
		double percent_color2;
		dist = point1.dist(new Point(x, y, null));
		percent_color2 = dist / fullDist;

		float red = new Float((point1.getColor().getRed()
				* (1 - percent_color2) + point2.getColor().getRed()
				* percent_color2) / 255);
		float green = new Float((point1.getColor().getGreen()
				* (1 - percent_color2) + point2.getColor().getGreen()
				* percent_color2) / 255);
		float blue = new Float((point1.getColor().getBlue()
				* (1 - percent_color2) + point2.getColor().getBlue()
				* percent_color2) / 255);
		Color color = new Color(red, green, blue);
		Point point = new Point(x, y, color);
		if (wireframe) {
			g.setColor(Color.RED);
		} else {
			Color matrixColor = this.colorMatrix[x][y];
			if (matrixColor == null) {
				matrixColor = color;
			}
			int blendedRed = (color.getRed() + matrixColor.getRed()) / 2;
			int blendedGreen = (color.getGreen() + matrixColor.getGreen()) / 2;
			int blendedBlue = (color.getBlue() + matrixColor.getBlue()) / 2;
			Color blendedColor = new Color(blendedRed, blendedGreen, blendedBlue);
			this.colorMatrix[x][y] = blendedColor;
			g.setColor(blendedColor);
		}

		g.drawRect(x, y, 1, 1);

		if (!wireframe && !point1.equals(firstPoint)) {
			drawLine(firstPoint, point, firstPoint, g, false);
		}
	}

	public List<Point> getPoints() {
		return points;
	}

	public void setPoints(List<Point> points) {
		this.points = points;
	}

	public Color getColor() {
		return color;
	}

	public void setColor(Color color) {
		this.color = color;
	}

	public Boolean isWireframeEnable() {
		return wireframeEnable;
	}

	public void setWireframeEnable(Boolean wireframe) {
		this.wireframeEnable = wireframe;
	}

	public void setWireframe(Wireframe wireframe) {
		this.wireframe = wireframe;
	}

	public Boolean isRasterizationEnable() {
		return rasterizationEnable;
	}

	public void setRasterizationEnable(Boolean rasterization) {
		this.rasterizationEnable = rasterization;
	}

	}
